Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/三件套/JS/JavaScript

03 Number与String

19 分钟阅读 · Note

目录树 578 篇

            • 00 JavaScript学习地图
            • 01 JavaScript基础
            • 02 类型系统与类型转换
            • 03 Number与String
            • 04 Array数组
            • 05 Object对象
            • 06 集合与迭代
            • 07 函数、作用域与this
            • 08 原型、构造函数与类
            • 09 Generator与异步迭代
            • 10 异步编程
            • 11 浏览器与DOM
            • 12 二进制与文件
            • 13 标准内建对象
            • 14 模块化
            • 15 错误处理
            • 16 JSDoc
            • 90 JavaScript知识点总目录
            • 91 JavaScript修正与补充记录
          • 前端模块化
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗00 JavaScript学习地图反向链接↗90 JavaScript知识点总目录反向链接↗91 JavaScript修正与补充记录反向链接↗01 JavaScript基础同一路径↗02 类型系统与类型转换同一路径↗04 Array数组同一路径
  • 03 Number与String

03 Number与String

> Last Format Time:8/11/2026 21:45:36

本笔记由两个 JavaScript 教程目录中的同主题内容合并而成;全部旧知识保留在“旧笔记知识全集(按来源保留)”中,并由导航逐项索引。


当前前端开发关键要点

> [!important] 学习优先级:P0 > - 理解 IEEE 754 浮点误差;金额与高精度计算不要直接依赖普通浮点运算。 > - 优先使用模板字符串、标准字符串 API 与明确的编码处理,避免隐式拼接造成类型问题。 > - 掌握 Number.isNaN、Number.isFinite、安全整数范围以及字符串不可变性。


知识点导航

[现代JS教程与阮一峰ES6 · 1数据类型与变量/Number](./03 Number与String.md)

  • Number API
  • 数字类型
  • 浮点数的底层实现
  • 总结

[现代JS教程与阮一峰ES6 · 1数据类型与变量/String](./03 Number与String.md)

  • String
  • 字符串

[廖雪峰教程 · 3快速入门/3.3 字符串](./03 Number与String.md)

  • 字符串
  • 多行字符串
  • 模板字符串
  • 操作字符串
  • toUpperCase
  • toLowerCase
  • indexOf
  • substring

现代前端补充与纠错

> [!info] 修改标记 > - 【修正】:旧教程中错误、过时或容易误导的内容。 > - 【补充】:旧教程未覆盖、但当前前端开发需要掌握的内容。 > - 【修正代码】:替换或校正了旧代码示例。

> [!warning] 下方保留旧教程上下文;已知语法错误已直接修正并标成 【修正代码】,API 差异标成 【修正】。

数值修正

【修正】 旧笔记中的科学计数法示例缺少赋值符号,正确写法是:

【修正代码】

const billion = 1e9;
  • 【修正】 Number("12px") 得到 NaN,parseInt("12px", 10) 得到 12;解析整数时显式传入基数。
  • 【补充】 浮点运算不能用十进制直觉直接比较,金额应使用最小货币单位整数、十进制定点库或明确的舍入规则。
  • 【修正】 用 Number.isNaN 判断真正的 NaN;全局 isNaN 会先强制转换。
  • 【补充】 国际化数字、货币、百分比、日期与相对时间优先使用 Intl,不要手拼区域格式。

字符串与 Unicode

  • 【补充】 length 和索引按 UTF-16 码元计数,不等同于用户看到的字符数;遍历码点可用 for...of,字素簇需使用 Intl.Segmenter 等方案。
  • 【补充】 用户输入搜索、排序、大小写转换要考虑 locale;展示层可使用 localeCompare/Intl.Collator。

旧笔记知识全集(按来源保留)

现代JS教程与阮一峰ES6 · 1数据类型与变量/Number

Number API

在JavaScript中,Number类型提供了许多静态方法和实例方法,用于处理数字相关的操作。以下是常用的Number API及实例:

一、静态方法(Number.xxx())

  1. Number.isFinite() 判断一个值是否为有限数(非Infinity、-Infinity或NaN)。
   console.log(Number.isFinite(123));      // true
   console.log(Number.isFinite(Infinity)); // false
   console.log(Number.isFinite(NaN));      // false
  1. Number.isInteger() 判断一个值是否为整数(不含小数部分)。
   console.log(Number.isInteger(5));    // true
   console.log(Number.isInteger(5.0));  // true(5.0本质是整数)
   console.log(Number.isInteger(5.5));  // false
  1. Number.isNaN() 判断一个值是否为NaN(比全局isNaN()更严格,不会强制类型转换)。
   console.log(Number.isNaN(NaN));       // true
   console.log(Number.isNaN('123'));     // false(全局isNaN会返回true)
   console.log(Number.isNaN(123));       // false
  1. Number.parseInt() 解析字符串并返回整数(与全局parseInt()功能相同,推荐优先使用)。
   console.log(Number.parseInt('123.45'));  // 123(只取整数部分)
   console.log(Number.parseInt('123abc'));  // 123(忽略非数字部分)
   console.log(Number.parseInt('abc123'));  // NaN(无法解析开头非数字)
  1. Number.parseFloat() 解析字符串并返回浮点数(与全局parseFloat()功能相同)。
   console.log(Number.parseFloat('123.45')); // 123.45
   console.log(Number.parseFloat('123abc')); // 123
  1. Number.MAX_SAFE_INTEGER / Number.MIN_SAFE_INTEGER 表示JavaScript中安全的最大/最小整数(安全指能精确表示且不与其他数值混淆)。
   console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
   console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991

二、实例方法(num.xxx())

  1. toFixed() 将数字转换为指定小数位数的字符串(四舍五入)。
   const num = 123.456;
   console.log(num.toFixed(2)); // "123.46"(保留2位小数)
   console.log(num.toFixed(0)); // "123"(保留0位小数)
  1. toString() 将数字转换为字符串,可指定基数(如2进制、16进制)。
   const num = 255;
   console.log(num.toString());   // "255"(默认10进制)
   console.log(num.toString(2));  // "11111111"(2进制)
   console.log(num.toString(16)); // "ff"(16进制,小写)
  1. toPrecision() 将数字转换为指定长度的字符串(包含整数和小数部分,四舍五入)。
   const num = 123.456;
   console.log(num.toPrecision(4)); // "123.5"(总长度4位)
   console.log(num.toPrecision(2)); // "1.2e+2"(科学计数法,总长度2位)
  1. valueOf() 返回数字的原始值(通常用于隐式类型转换)。
   const numObj = new Number(123);
   console.log(numObj.valueOf()); // 123(从包装对象中获取原始值)
  1. toLocaleString() 根据本地环境格式化数字(如千分位分隔符)。
   const num = 1234567.89;
   console.log(num.toLocaleString()); // "1,234,567.89"(默认本地格式)
   console.log(num.toLocaleString('de-DE')); // "1.234.567,89"(德语环境格式)
数字类型

JavaScript 中的常规数字以 64 位的格式 IEEE-754 存储,就是“双精度浮点数” 常规整数不能安全地超过 2^53-1 或小于 -2^53-1。由于仅在少数特殊领域才会用到 BigInt,因此我们在特殊的章节 BigInt 中对其进行了介绍。

使用下划线更具可读性。

log(1_000_000);//1000000

使用e: 8/11/26 【修正代码】原内容:let billion 1e9;修改点:补上赋值号。

let billion = 1e9;//10亿
let mcs = 1e-6;

进制: 十六进制 数字在 JavaScript 中被广泛用于表示颜色,编码字符以及其他许多东西。所以自然地,有一种较短的写方法:0x,然后是数字。 二进制和八进制数字系统很少使用,但也支持使用 0b 和 0o 前缀

let a = 0xff;   //16进制
let b = 0b11111; //2进制
let c = 0o377;   //8进制

let num = 255;
log(num.toString(16));//ff

请注意 123456..toString(36) 中的两个点不是打错了。如果我们想直接在一个数字上调用一个方法,比如上面例子中的 toString,那么我们需要在它后面放置两个点 ..。

如果我们放置一个点:123456.toString(36),那么就会出现一个 error,因为 JavaScript 语法隐含了第一个点之后的部分为小数部分。如果我们再放一个点,那么 JavaScript 就知道小数部分为空,现在使用该方法。

也可以写成 (123456).toString(36)。

舍入: Math.floor 向下舍入:3.1 变成 3,-1.1 变成 -2。

Math.ceil 向上舍入:3.1 变成 4,-1.1 变成 -1。

Math.round 向最近的整数舍入:3.1 变成 3,3.6 变成 4,中间值 3.5 变成 4。

Math.trunc(IE 浏览器不支持这个方法) 移除小数点后的所有内容而没有舍入:3.1 变成 3,-1.1 变成 -1。

Math.floorMath.ceilMath.roundMath.trunc
3.1343
3.6344
-1.1-2-1-1
-1.6-2-1-2

我们有 1.2345,并且想把它舍入到小数点后两位,仅得到 1.23。 有两种方式可以实现这个需求:

  1. 乘除法 例如,要将数字舍入到小数点后两位,我们可以将数字乘以 100,调用舍入函数,然后再将其除回。
  2. 函数 toFixed(n) 将数字舍入到小数点后 n 位,并以字符串形式返回结果。类似于 Math.round
let num = 12.345;

log(Math.floor(num * 100));//1234
log(num.toFixed(9));//12.345000000

不精确的计算: 如果一个数字真的很大,则可能会溢出 64 位存储,变成一个特殊的数值 Infinity 如果我们检查 0.1 和 0.2 的总和是否为 0.3,我们会得到 false。

log(0.1 + 0.2 == 0.3);//false
log(0.1 + 0.2); //0.30000000000000004
log(0.3);//0.3

十进制转二进制计算后再转十进制输出导致的误差,二进制无法准确的存储0.1与0.2

function equal (a, b) {
	return Math.abs(a - b) < Number.EPSILON
}
浮点数的底层实现
  1. 最大数(Number.MAX_VALUE)
  • 值:1.7976931348623157e+308

  • 原理: IEEE 754 双精度浮点数的结构为:1位符号位 + 11位指数位 + 52位尾数位。

    • 指数位最大值为 2^11 - 1 = 2047,但标准规定指数 2047 用于表示特殊值(如 Infinity),因此有效最大指数为 2046。
    • 尾数位最大值为 2^52 - 1(全为1),尾数值为 1 + (2^52 - 1)/2^52 ≈ 2(接近2但小于2)。
    • 计算公式:(2 - 2^-52) × 2^2046 ≈ 1.7976931348623157e+308。
  • 含义:大于此值的数会被表示为 Infinity(无穷大)。

  console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
  console.log(Number.MAX_VALUE * 2); // Infinity
  1. 最大安全整数(Number.MAX_SAFE_INTEGER)
  • 值:9007199254740991(即 2^53 - 1)

  • 原理: 双精度浮点数的尾数位只有52位(加上隐含的1位,共53位有效数字)。这意味着:

    • 对于整数,只有在 -2^53 到 2^53 范围内的数,才能被精确表示。
    • 超过 2^53 的整数可能无法与相邻整数区分(例如 2^53 + 1 会被舍入为 2^53)。
  • 含义:“安全”指该整数及以内的所有整数都能被精确表示,且不会与其他整数混淆。

  console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
  console.log(2 **53 === 2** 53 + 1); // true(两者无法区分,均为 9007199254740992)
  1. 最小精度值(Number.EPSILON)
  • 值:2.220446049250313e-16(即 2^-52)

  • 原理: 尾数位的最小精度单位是 2^-52。对于 1 这个数,它与下一个可表示的浮点数(1 + 2^-52)之间的差值就是 EPSILON。

  • 含义:表示 JavaScript 中能区分的两个相邻浮点数的最小差值,常用于判断浮点数运算的精度误差。

  console.log(Number.EPSILON); // 2.220446049250313e-16

  // 示例:判断两个浮点数是否近似相等
  function isEqual(a, b) {
    return Math.abs(a - b) < Number.EPSILON;
  }
  console.log(0.1 + 0.2 === 0.3); // false(浮点数误差)
  console.log(isEqual(0.1 + 0.2, 0.3)); // true(通过 EPSILON 判断)
总结
常量值本质原因用途场景
MAX_VALUE~1.797e+30864位浮点数的指数位和尾数位限制判断数值是否溢出为无穷大
MAX_SAFE_INTEGER2^53 - 153位有效数字的精度限制确保整数运算精确性
EPSILON2^-52浮点数的最小精度单位处理浮点数运算的精度误差

这些限制源于 JavaScript 对浮点数的底层实现,了解它们有助于避免数值计算中的常见陷阱(如精度丢失、整数溢出)。

使用二进制数字系统无法 精确 存储 0.1 或 0.2 最可靠的方法是借助方法 toFixed(n) 对结果进行舍入

let money = 0.1 + 0.2;
let fixedMoney = +money.toFixed(2);

log(fixedMoney);//0.3
log(typeof fixedMoney);//number

特殊的数值:

  • Infinity(和 -Infinity)是一个特殊的数值,比任何数值都大(小)。
  • NaN 代表一个 error。

它们属于 number 类型,但不是“普通”数字,有用于检查它们的特殊函数:

  • isNaN(value) 将其参数转换为数字,然后测试它是否为 NaN
  • 我们不能只使用 === NaN 比较吗?很不幸,这不行。值 “NaN” 是独一无二的,它不等于任何东西,包括它自身
log(NaN == NaN);//false
log(NaN === NaN);//false
log(isNaN(Infinity), isNaN(-Infinity));//Infinity和-Infinity都是数字
  • isFinite(value) 将其参数转换为数字,如果是常规数字而不是 NaN/Infinity/-Infinity,则返回 true
  • 在所有数字函数中,包括 isFinite,空字符串或仅有空格的字符串均被视为 0

parseInt parseFloat: 使用加号 + 或 Number() 的数字转换是严格的。如果一个值不完全是一个数字,就会失败. 唯一的例外是字符串开头或结尾的空格,因为它们会被忽略。

parseInt 和 parseFloat 可以从字符串中“读取”数字,直到无法读取为止。如果发生 error,则返回收集到的数字。函数 parseInt 返回一个整数,而 parseFloat 返回一个浮点数 如 CSS 中的 "100px" 或 "12pt"。并且,在很多国家,货币符号是紧随金额之后的,所以我们有 "19€",并希望从中提取出一个数值

parseInt() 函数具有可选的第二个参数。它指定了数字系统的基数,因此 parseInt 还可以解析十六进制数字、二进制数字等的字符串

log(parseInt('20px', 10));//20
log(typeof parseInt('20px', 10));//number

JavaScript 有一个内建的 Math 对象,它包含了一个小型的数学函数和常量库。 Math.random() 返回一个从 0 到 1 的随机数(不包括 1)。

function random(min,max){
	return min + Math.random() * (max - min)
}

Math.max(a, b, c...) 和 Math.min(a, b, c...) 从任意数量的参数中返回最大值和最小值。

Math.pow(n, power) 返回 n 的给定(power)次幂。

现代JS教程与阮一峰ES6 · 1数据类型与变量/String

String

一、获取与查找

  1. charAt(index) 返回指定索引位置的字符(索引从0开始)。
   const str = "hello";
   console.log(str.charAt(1)); // "e"(索引1的字符)
   console.log(str.charAt(10)); // ""(索引超出范围返回空字符串)
  1. indexOf(searchValue, fromIndex) 查找子串首次出现的索引,未找到返回-1,fromIndex指定起始位置。
   const str = "hello world";
   console.log(str.indexOf("o")); // 4(首次出现"o"的位置)
   console.log(str.indexOf("o", 5)); // 7(从索引5开始查找)
  1. lastIndexOf(searchValue) 查找子串最后一次出现的索引,未找到返回-1。
   const str = "hello world";
   console.log(str.lastIndexOf("o")); // 7(最后出现"o"的位置)
  1. includes(searchValue, fromIndex) 判断字符串是否包含指定子串,返回布尔值。
   const str = "hello";
   console.log(str.includes("ll")); // true
   console.log(str.includes("xyz")); // false
  1. startsWith(searchValue, fromIndex) / endsWith(searchValue, length) 判断字符串是否以指定子串开头/结尾。
   const str = "hello world";
   console.log(str.startsWith("he")); // true
   console.log(str.endsWith("ld")); // true
   console.log(str.endsWith("lo", 5)); // true(只看前5个字符"hello")

二、截取与分割

  1. slice(startIndex, endIndex) 截取从startIndex到endIndex(不包含)的子串,支持负数索引(从末尾计算)。
   const str = "abcdef";
   console.log(str.slice(1, 4)); // "bcd"(索引1到3)
   console.log(str.slice(-3)); // "def"(从倒数第3位到结尾)
  1. substring(startIndex, endIndex) 类似slice,但不支持负数索引,且自动调整参数顺序(如start &gt; end则交换)。
   const str = "abcdef";
   console.log(str.substring(4, 1)); // "bcd"(自动交换为1到4)
  1. split(separator, limit) 按分隔符分割字符串为数组,limit限制返回的数组长度。
   const str = "apple,banana,orange";
   console.log(str.split(",")); // ["apple", "banana", "orange"]
   console.log(str.split(",", 2)); // ["apple", "banana"](只取前2个)

三、转换与修改

  1. toUpperCase() / toLowerCase() 转换字符串为全大写/全小写。
   const str = "Hello World";
   console.log(str.toUpperCase()); // "HELLO WORLD"
   console.log(str.toLowerCase()); // "hello world"
  1. trim() / trimStart() / trimEnd() 去除字符串两端/开头/结尾的空白字符(空格、换行等)。
   const str = "  hello  ";
   console.log(str.trim()); // "hello"(去除两端空白)
   console.log(str.trimStart()); // "hello  "(仅去除开头空白)
  1. replace(searchValue, replacement) 替换匹配的子串(默认只替换第一个匹配项,可用正则/g全局替换)。
   const str = "cat dog cat";
   console.log(str.replace("cat", "bird")); // "bird dog cat"(替换第一个)
   console.log(str.replace(/cat/g, "bird")); // "bird dog bird"(全局替换)
  1. repeat(count) 将字符串重复count次并返回新字符串。
   const str = "ab";
   console.log(str.repeat(3)); // "ababab"

四、其他常用方法

  1. length(属性) 返回字符串的长度(字符数量)。
   const str = "hello";
   console.log(str.length); // 5
  1. padStart(targetLength, padString) / padEnd(targetLength, padString) 在字符串开头/结尾填充指定字符,直到达到目标长度。
   const str = "123";
   console.log(str.padStart(5, "0")); // "00123"(开头补0至长度5)
   console.log(str.padEnd(5, "-")); // "123--"(结尾补-至长度5)
  1. concat(str1, str2, ...) 拼接多个字符串(推荐直接使用+或模板字符串更简洁)。
   const str1 = "hello";
   const str2 = "world";
   console.log(str1.concat(" ", str2)); // "hello world"

总结String的API覆盖了字符串的查找、截取、转换、修改等常见操作,实际开发中可根据场景选择:

  • 查找子串用includes、indexOf;
  • 截取用slice(推荐,支持负数索引);
  • 格式化用trim、toUpperCase、padStart等;
  • 替换用replace(结合正则更强大)。

以下是 str.toUpperCase() 中实际发生的情况:

  1. 字符串 str 是一个原始值。因此,在访问其属性时,会创建一个包含字符串字面值的特殊对象,并且具有可用的方法,例如 toUpperCase()。
  2. 该方法运行并返回一个新的字符串(由 alert 显示)。
  3. 特殊对象被销毁,只留下原始值 str。

所以原始类型可以提供方法,但它们依然是轻量级的。 JavaScript 引擎高度优化了这个过程。它甚至可能跳过创建额外的对象。但是它仍然必须遵守规范,并且表现得好像它创建了一样。

特殊的原始类型 null 和 undefined 是例外。它们没有对应的“对象包装器”,也没有提供任何方法。从某种意义上说,它们是“最原始的”。

字符串

在 JavaScript 中,文本数据被以字符串形式存储,单个字符没有单独的类型。 字符串的内部格式始终是 UTF-16,它不依赖于页面编码。

字符串可以包含在单引号、双引号或反引号中 反引号允许我们通过 ${…} 将任何表达式嵌入到字符串中。反引号的另一个优点是它们允许字符串跨行

log(`shit
    shjit
    shit
    `);
/**
 * shit
    shjit
    shit
 */

反引号还允许我们在第一个反引号之前指定一个“模版函数”。语法是:func`string`。函数 func 被自动调用,接收字符串和嵌入式表达式,并处理它们。你可以在 docs 中阅读更多关于它们的信息。这叫做 “tagged templates”。此功能可以更轻松地将字符串包装到自定义模版或其他函数中,但这很少使用。

字符描述
\n换行
\r在 Windows 文本文件中,两个字符 \r\n 的组合代表一个换行。而在非 Windows 操作系统上,它就是 \n。这是历史原因造成的,大多数的 Windows 软件也理解 \n。
\', \"引号
\\反斜线
\t制表符
\b, \f, \v退格,换页,垂直标签 —— 为了兼容性,现在已经不使用了。
\xXX具有给定十六进制 Unicode XX 的 Unicode 字符,例如:'\x7A' 和 'z' 相同。
\uXXXX以 UTF-16 编码的十六进制代码 XXXX 的 Unicode 字符,例如 \u00A9 —— 是版权符号 © 的 Unicode。它必须正好是 4 个十六进制数字。
\u{X…XXXXXX}(1 到 6 个十六进制字符)具有给定 UTF-32 编码的 Unicode 符号。一些罕见的字符用两个 Unicode 符号编码,占用 4 个字节。这样我们就可以插入长代码了。
log("\u00A9");//©
log("\u{20331}");//𠌱
log("\u{1f60d}");//😍
log("\u{1F60D}");//😍 大小写一样

字符串长度: length 属性表示字符串长度。这是一个属性不是函数。

const str = 'shit';
log(str.length);//4

要获取在 pos 位置的一个字符,可以使用方括号 [pos] 或者调用 str.charAt(pos) 方法。第一个字符从零位置开始 方括号是获取字符的一种现代化方法,而 charAt 是历史原因才存在的。 它们之间的唯一区别是,如果没有找到字符,[] 返回 undefined,而 charAt 返回一个空字符串

log(str[5]);//undefined
log(str.charAt(5));// "" 空的字符串

在 JavaScript 中,字符串不可更改。改变字符是不可能的。

toLowerCase() 和 toUpperCase() 方法可以改变大小写

查找子字符串: 第一个方法是 str.indexOf(substr, pos)。 它从给定位置 pos 开始,在 str 中查找 substr,如果没有找到,则返回 -1,否则返回匹配成功的位置。可选的第二个参数允许我们从一个给定的位置开始检索。

还有一个类似的方法 str.lastIndexOf(substr, position),它从字符串的末尾开始搜索到开头。 它会以相反的顺序列出这些事件。

在 if 测试中 indexOf 有一点不方便。我们不能像这样把它放在 if 中 ![Pasted image 20250624185221](../../../../_assets/3897419-Pasted image 20250624185221.png) 这里使用的一个老技巧是 bitwise NOT ~ 运算符。它将数字转换为 32-bit 整数(如果存在小数部分,则删除小数部分),然后对其二进制表示形式中的所有位均取反。

实际上,这意味着一件很简单的事儿:对于 32-bit 整数,~n 等于 -(n+1)。不推荐使用。

更现代的方法 str.includes(substr, pos) 根据 str 中是否包含 substr 来返回 true/false。 如果我们需要检测匹配,但不需要它的位置,那么这是正确的选择:

log(str.includes("s"));//true

方法 str.startsWith 和 str.endsWith 的功能与其名称所表示的意思相同

JavaScript 中有三种获取字符串的方法:substring、substr 和 slice。 str.slice(start [, end]) 返回字符串从 start 到(但不包括)end 的部分。如果没有第二个参数,slice 会一直运行到字符串末尾。start/end 也有可能是负值。它们的意思是起始位置从字符串结尾计算。

str.substring(start [, end]) 返回字符串从 start 到(但不包括)end 的部分。 这与 slice 几乎相同,但它允许 start 大于 end。

str.substr(start [, length]) 返回字符串从 start 开始的给定 length 的部分。 与以前的方法相比,这个允许我们指定 length 而不是结束位置 第一个参数可能是负数,从结尾算起

方法选择方式……负值参数
slice(start, end)从 start 到 end(不含 end)允许
substring(start, end)从 start 到 end(不含 end)负值被视为 0
substr(start, length)从 start 开始获取长为 length 的字符串允许 start 为负数
仅仅记住这三种方法中的 slice 就足够了。

JavaScript 共有 6 种方法可以表示一个字符。

'\z' === 'z'  // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true

为字符串添加了遍历器接口(详见《Iterator》一章),使得字符串可以被for...of循环遍历。

for (let codePoint of 'foo') {
  console.log(codePoint)
}
// "f"
// "o"
// "o"

除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。

let text = String.fromCodePoint(0x20BB7);

for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// " "
// " "

for (let i of text) {
  console.log(i);
}
// "𠮷"

上面代码中,字符串text只有一个字符,但是for循环会认为它包含两个字符(都不可打印),而for...of循环会正确识别出这一个字符。

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

上面代码中的模板字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。在${}中可以放入任意的表达式,还能调用函数。如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。甚至还能嵌套使用

如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。

$('#list').html(`
<ul>
  <li>first</li>
  <li>second</li>
</ul>
`);

上面代码中,所有模板字符串的空格和换行,都是被保留的,比如&lt;ul&gt;标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。

$('#list').html(`
<ul>
  <li>first</li>
  <li>second</li>
</ul>
`.trim());

ES6 提供了String.fromCodePoint()方法,可以识别大于0xFFFF的字符,弥补了String.fromCharCode()方法的不足。在作用上,正好与下面的codePointAt()方法相反。

String.fromCodePoint(0x20BB7)
// "𠮷"
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
// true

JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。

var s = "𠮷";

s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271

字符可以通过charCodeAt方法获取其==Unicode==编码

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign()方法可以很方便地一次向类添加多个方法。

class Point {
  constructor(){
    // ...
  }
}

Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});

constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。

class Point {
}

// 等同于
class Point {
  constructor() {}
}

廖雪峰教程 · 3快速入门/3.3 字符串

字符串

JavaScript的字符串就是用''或""括起来的字符表示。

如果'本身也是一个字符,那就可以用""括起来,比如"I'm OK"包含的字符是I,',m,空格,O,K这6个字符。

如果字符串内部既包含'又包含"怎么办?可以用转义字符\来标识,比如:

'I\'m \"OK\"!'; // I'm "OK"!

表示的字符串内容是:I'm "OK"!

转义字符\可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\\表示的字符就是\。

ASCII字符可以以\x##形式的十六进制表示,例如:

'\x41'; // 完全等同于 'A'

还可以用\u####表示一个Unicode字符:

'\u4e2d\u6587'; // 完全等同于 '中文'
多行字符串

由于多行字符串用\n写起来比较费事,所以最新的ES6标准新增了一种多行字符串的表示方法,用反引号...表示:

`这是一个
多行
字符串`;

注意:反引号在键盘的ESC下方,数字键1的左边:

┌─────┐ ┌─────┬─────┬─────┬─────┐
│ ESC │ │ F1  │ F2  │ F3  │ F4  │
└─────┘ └─────┴─────┴─────┴─────┘
┌─────┬─────┬─────┬─────┬─────┐
│  ~  │  !  │  @  │  #  │  $  │
│  `  │  1  │  2  │  3  │  4  │
├─────┴──┬──┴──┬──┴──┬──┴──┬──┘
│        │     │     │     │
│  tab   │  Q  │  W  │  E  │
├────────┴──┬──┴──┬──┴──┬──┘
│           │     │     │
│ caps lock │  A  │  S  │
└───────────┴─────┴─────┘
模板字符串

要把多个字符串连接起来,可以用+号连接:

let name = '小明';
let age = 20;
let message = '你好, ' + name + ', 你今年' + age + '岁了!';
alert(message);

如果有很多变量需要连接,用+号就比较麻烦。ES6新增了一种模板字符串,表示方法和上面的多行字符串一样,但是它会自动替换字符串中的变量:

let name = '小明';
let age = 20;
let message = `你好, ${name}, 你今年${age}岁了!`;
alert(message);
操作字符串

获取字符串长度:

let s = 'Hello, world!';
s.length; // 13

要获取字符串某个指定位置的字符,使用类似Array的下标操作,索引号从0开始:

let s = 'Hello, world!';

s[0]; // 'H'
s[6]; // ' '
s[7]; // 'w'
s[12]; // '!'
s[13]; // undefined 超出范围的索引不会报错,但一律返回undefined

需要特别注意的是,字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果:

let s = 'Test';
s[0] = 'X';
console.log(s); // s仍然为'Test'

JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串:

toUpperCase

toUpperCase()把一个字符串全部变为大写:

let s = 'Hello';
s.toUpperCase(); // 返回'HELLO'
toLowerCase

toLowerCase()把一个字符串全部变为小写:

let s = 'Hello';
let lower = s.toLowerCase(); // 返回'hello'并赋值给变量lower
lower; // 'hello'
indexOf

indexOf()会搜索指定字符串出现的位置:

let s = 'hello, world';
s.indexOf('world'); // 返回7
s.indexOf('World'); // 没有找到指定的子串,返回-1
substring

substring()返回指定索引区间的子串:

let s = 'hello, world'
s.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello'
s.substring(7); // 从索引7开始到结束,返回'world'